﻿#include "dgram.h"
#include "mblock.h"
#include "nlist.h"
#include "debug.h"
#include "socket.h"
#include "udp.h"
#include "tools.h"
#include "protocol.h"

static dgram_t dgram_tbl[DGRAM_MAX_NR]; // 存放dgram类型的socket
static mblock_t dgram_mblock;           // 内存管理器
static nlist_t dgram_list;

#if DBG_DISP_ENABLED(DBG_DGRAM_LEVEL)
static void display_dgram_list()
{
    plat_printf("-----------------dgram_list-----------------\n");
    nlist_node_t *p;
    int index = 0;
    nlist_for_each(p, &dgram_list)
    {
        plat_printf("================================\n");
        dgram_t *dgram = (dgram_t *)nlist_entry(sock_t, node, p);
        plat_printf("[%d]:\n", index++);
        dbg_dump_ipaddr_oneline("local_ip:", &dgram->base.local_ipaddr);
        plat_printf("  local_port: %d\n", dgram->base.local_port);
        dbg_dump_ipaddr_oneline("remote_ip:", &dgram->base.remote_ipaddr);
        plat_printf("  remote_port: %d\n", dgram->base.remote_port);
        plat_printf("================================\n");
    }
    plat_printf("-----------------------------------------\n");
}
#else
#define display_dgram_list()
#endif

/**
 * @brief 判断端口是否被使用
 */
static bool is_port_used(int port)
{
    nlist_node_t *p;
    nlist_for_each(p, &dgram_list)
    {
        sock_t *sock = (sock_t *)nlist_entry(sock_t, node, p);
        if (sock->local_port == port)
        {
            return true;
        }
    }

    return false;
}

static net_err_t alloc_port(sock_t *sock)
{
    static int search_index = NET_PORT_DYN_START;
    for (int i = NET_PORT_DYN_START; i <= NET_PORT_DYN_END; ++i)
    {
        int port = search_index++;
        if (search_index > NET_PORT_DYN_END)
        {
            search_index = NET_PORT_DYN_START;
        }
        if (!is_port_used(port))
        {
            sock->local_port = port;
            return NET_ERR_OK;
        }
    }

    return NET_ERR_NULLP;
}

/**
 * @brief dgram类型初始化
 */
net_err_t dgram_init()
{
    DBG_INFO(DGRAM_MAX_NR, "dgram_init begin!");
    nlist_init(&dgram_list);
    mblock_init(&dgram_mblock, dgram_tbl, sizeof(dgram_t), DGRAM_MAX_NR, NLOCKER_NONE);
    DBG_INFO(DGRAM_MAX_NR, "dgram_init end!");

    return NET_ERR_OK;
}

/**
 * @brief dgram类型socket发送数据时调用的函数
 */
static net_err_t dgram_sendto(struct _sock_t *s, const void *buf, size_t len, int flags,
                              const struct x_sockaddr *dest, x_socklen_t dest_len, ssize_t *result_len)
{
    pktbuf_t *pktbuf = pktbuf_alloc((u_int)len);
    if (!pktbuf)
    {
        DBG_ERROR(DBG_DGRAM_LEVEL, "alloc pktbuf size:%d falied!", len);
        return NET_ERR_FULL;
    }

    net_err_t err = pktbuf_write(pktbuf, (const uint8_t *)buf, (int)len);
    if (err < NET_ERR_OK)
    {
        DBG_ERROR(DBG_DGRAM_LEVEL, "write buf failed in dgram sendto!");
        goto end_send_to;
    }

    ipaddr_t dest_ipaddr;
    const struct x_sockaddr_in *sockaddr = (const struct x_sockaddr_in *)dest;
    dest_ipaddr = ipaddr_from_buf(sockaddr->sin_addr.add_array, IPADDR_V4);
    if (!ipaddr_is_any(&s->remote_ipaddr) && !ipaddr_is_equal(&s->remote_ipaddr, &dest_ipaddr))
    {
        DBG_ERROR(DBG_DGRAM_LEVEL, "socket's remote ipaddr doesn't equal to dest ipaddr!");
        err = NET_ERR_MEM;
        goto end_send_to;
    }

    int dport = x_ntohs(sockaddr->sin_port);
    if (s->remote_port && (s->remote_port != dport))
    {
        DBG_ERROR(DBG_DGRAM_LEVEL, "socket's remote port doesn't equal to dest port!");
        err = NET_ERR_UNREACH;
        goto end_send_to;
    }

    if (!s->local_port && (alloc_port(s) < NET_ERR_OK))
    {
        DBG_ERROR(DBG_DGRAM_LEVEL, "socket's local port has some errors!");
        err = NET_ERR_FULL;
        goto end_send_to;
    }

    // 发送数据
    switch (s->protocol)
    {
    case NET_PROTOCOL_UDP:
    {
        err = udp_out(&dest_ipaddr, dport, &s->local_ipaddr, s->local_port, pktbuf);
        if (err < NET_ERR_OK)
        {
            DBG_ERROR(DBG_DGRAM_LEVEL, "dgram socket send pkt by udp protocol failed!");
            goto end_send_to;
        }
        *result_len = (ssize_t)len;
        break;
    }
    default:
        err = NET_ERR_UNREACH;
        goto end_send_to;
    }

    return NET_ERR_OK;

end_send_to:
    *result_len = 0;
    pktbuf_free(pktbuf);
    return err;
}

/**
 * @dgram类型recv_from调用函数
 */
static net_err_t dgram_recvfrom(struct _sock_t *s, void *buf, size_t len, int flags,
                                struct x_sockaddr *dst, x_socklen_t *dst_len, ssize_t *result_len)
{
    dgram_t *dgram = (dgram_t *)s;
    nlist_node_t *first = nlist_remove_first(&dgram->recv_list);
    if (!first)
    {
        *result_len = 0;
        return NET_ERR_NEEDWAIT;
    }
    pktbuf_t *pktbuf = (pktbuf_t *)nlist_entry(pktbuf_t, node, first);

    switch (s->protocol)
    {
    case NET_PROTOCOL_UDP:
    {
        udp_from_t *from = (udp_from_t *)pktbuf_data(pktbuf);
        struct x_sockaddr_in *addr = (struct x_sockaddr_in *)dst;
        plat_memset(addr, 0, sizeof(struct x_sockaddr_in));
        addr->sin_family = AF_INET;
        addr->sin_port = from->port;
        addr->sin_addr.s_addr = from->ip.q_addr;
        addr->sin_len = IPV4_ADDR_SIZE;
        pktbuf_remove_header(pktbuf, sizeof(udp_from_t));
        break;
    }
    default:
        break;
    }

    pktbuf_reset_acc(pktbuf);
    int read_size = len > pktbuf->total_size ? pktbuf->total_size : (int)len;
    net_err_t err = pktbuf_read(pktbuf, buf, read_size, (int)len);
    if (err < NET_ERR_OK)
    {
        DBG_ERROR(DBG_DGRAM_LEVEL, "read pktbuf failed in dgram recv from!");
        pktbuf_free(pktbuf);
        return NET_ERR_SYS;
    }

    pktbuf_free(pktbuf);
    *result_len = read_size;
    return NET_ERR_OK;
}

/**
 * @brief dgram类型socket调用的connect接口
 */
static net_err_t dgram_connect(struct _sock_t *s, const struct x_sockaddr *addr, x_socklen_t addrlen)
{
    net_err_t err = sock_connect(s, addr, addrlen);
    display_dgram_list();
    return err;
}

/**
 * @brief dgram类型socket调用的bind接口
 */
static net_err_t dgram_bind(struct _sock_t *s, const struct x_sockaddr *addr, x_socklen_t addrlen)
{
    // 检查地址是否有效 ip地址可以为缺省值，同一IP下端口不能重复使用
    const struct x_sockaddr_in *bind_addr = (const struct x_sockaddr_in *)addr;
    if (s->local_port)
    {
        DBG_ERROR(DBG_DGRAM_LEVEL, "the port:%d has been binded!", s->local_port);
        return NET_ERR_BINDED;
    }
    int bind_port = x_ntohs(bind_addr->sin_port);
    ipaddr_t bind_ip;
    bind_ip.q_addr = bind_addr->sin_addr.s_addr;

    nlist_node_t *p;
    nlist_for_each(p, &dgram_list)
    {
        dgram_t *tmp_dgram = (dgram_t *)nlist_entry(sock_t, node, p);
        // 忽略自身
        if((sock_t *)tmp_dgram == s)
        {
            continue;
        }

        // ip和端口都相同，则该端口已绑定，无法再次绑定
        if(s->local_port == bind_port && ipaddr_is_equal(&bind_ip, &s->local_ipaddr))
        {
            DBG_ERROR(DBG_DGRAM_LEVEL, "the port:%d has been binded!", s->local_port);
            return NET_ERR_BINDED;
        }
    }

    net_err_t err = sock_bind(s, &bind_ip, bind_port);
    if(err < NET_ERR_OK)
    {
        DBG_ERROR(DBG_DGRAM_LEVEL, "bind failed!");
        return err;
    }

    display_dgram_list();
    return NET_ERR_OK;
}

/**
 * @brief dgram_dgram类型socket调用的close接口
 */
static net_err_t dgram_close(struct _sock_t *sock)
{
    dgram_t *dgram = (dgram_t *)sock;

    // socket链表中移除该socket
    nlist_remove(&dgram_list, &sock->node);
    // 清除socket中的缓存
    nlist_node_t *first_node;
    while (first_node = nlist_remove_first(&dgram->recv_list))
    {
        pktbuf_t *pktbuf = (pktbuf_t *)nlist_entry(pktbuf_t, node, first_node);
        pktbuf_free(pktbuf);
    }
    // 释放信号量
    sock_uninit(sock);
    // 释放socket
    mblock_free(&dgram_mblock, dgram);

    display_dgram_list();
    return NET_ERR_OK;
}

sock_t *dgram_create(int family, int protocol)
{
    const static sock_ops_t ops = {
        .setopt = sock_setopt,
        .sendto = dgram_sendto,
        .send = sock_send,
        .recvfrom = dgram_recvfrom,
        .recv = sock_recv,
        .connect = dgram_connect,
        .bind = dgram_bind,
        .close = dgram_close};
    dgram_t *dgram = (dgram_t *)mblock_alloc(&dgram_mblock, DELIVER_NO_WAITTIME);
    if (!dgram)
    {
        DBG_WARNING(DBG_DGRAM_LEVEL, "alloc dgram failed!");
        return NULL;
    }
    nlist_init(&dgram->recv_list);
    net_err_t err = sock_init(&dgram->base, family, protocol, &ops);
    if (err < NET_ERR_OK)
    {
        DBG_ERROR(DBG_DGRAM_LEVEL, "sock init failed in dgram create!");
        return NULL;
    }

    dgram->base.recv_wait = &dgram->recv_wait;
    if (sock_wait_init(dgram->base.recv_wait) < NET_ERR_OK)
    {
        DBG_ERROR(DBG_DGRAM_LEVEL, "recv wait init failed in dgram create!");
        mblock_free(&dgram_mblock, dgram);
        sock_uninit(&dgram->base);
        return NULL;
    }

    nlist_insert_last(&dgram_list, &dgram->base.node);

    display_dgram_list();
    return (sock_t *)dgram;
}

net_err_t dgram_in(pktbuf_t *buf)
{
    return NET_ERR_OK;
}

/**
 * @brief 查找是否存在对应的dgram类型的socket
 * @param src_ip socket源ip地址
 * @param sport socket源端口
 * @param dst_ip socket目的ip地址
 * @param dport socket目的端口
 */
dgram_t *dgram_find(ipaddr_t *src_ip, uint16_t sport, ipaddr_t *dst_ip, uint16_t dport)
{
    nlist_node_t *p;
    nlist_for_each(p, &dgram_list)
    {
        dgram_t *curr_dgram = (dgram_t *)nlist_entry(sock_t, node, p);
        if (!ipaddr_is_any(&curr_dgram->base.local_ipaddr) && !ipaddr_is_equal(&curr_dgram->base.local_ipaddr, src_ip))
        {
            continue;
        }

        if (curr_dgram->base.local_port && curr_dgram->base.local_port != sport)
        {
            continue;
        }

        if (!ipaddr_is_any(&curr_dgram->base.remote_ipaddr) && !ipaddr_is_equal(&curr_dgram->base.remote_ipaddr, dst_ip))
        {
            continue;
        }

        if (curr_dgram->base.remote_port && curr_dgram->base.remote_port != dport)
        {
            continue;
        }

        return (dgram_t *)curr_dgram;
    }

    return NULL;
}

/**
 * @brief 向对应的socket中的读取缓存中加入数据包
 */
net_err_t dgram_recvbuf_add(dgram_t *dgram, pktbuf_t *buf)
{
    if (nlist_count(&dgram->recv_list) < DGRAM_MAX_RECV)
    {
        nlist_insert_last(&dgram->recv_list, &buf->node);
        sock_wakeup(&dgram->base, SOCK_WAIT_READ, NET_ERR_OK);
        return NET_ERR_OK;
    }
    else
    {
        DBG_WARNING(DBG_DGRAM_LEVEL, "the socket recv list is full!");
        return NET_ERR_FULL;
    }
}